import gzip
import zlib

import pycurl
from io import BytesIO
from typing import Optional, Dict, Any, Callable, Union
import json



#pycurl.global_init(pycurl.GLOBAL_DEFAULT)  #模块级初始化
class HttpUtil:
    def __init__(self):
        self.c = pycurl.Curl()
        
        self.lastProxy=None
        #self.c.setopt(pycurl.VERBOSE, 1)  # 启用调试输出
    def __del__(self):
        if hasattr(self, 'c') and self.c:
            self.c.close()
        if hasattr(self, 's'):
            self.s=None
        if hasattr(self, 'mc') and self.mc:
            try:
                self.mc.close()
            except Exception:
                pass
            self.mc = None
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if hasattr(self, 'c') and self.c:
            self.c.close()
        if hasattr(self, 's'):
            self.s=None
        if hasattr(self, 'mc') and self.mc:
            try:
                self.mc.close()
            except Exception:
                pass
            self.mc = None
        return False  # 不处理异常，继续抛出
    def decompress_data(self, compressed_data: bytes, content_encoding: str) -> bytes:
        """
        解压缩数据
        :param compressed_data: 压缩的数据
        :param content_encoding: Content-Encoding 头信息
        :return: 解压后的数据
        """
        try:
            if content_encoding == 'gzip':
                return gzip.decompress(compressed_data)
            elif content_encoding == 'deflate':
                return zlib.decompress(compressed_data)
            elif content_encoding == 'br':
                # 如果需要支持brotli，可以安装brotli库
                # import brotli
                # return brotli.decompress(compressed_data)
                raise ValueError("Brotli compression is not supported")
            else:
                return compressed_data
        except Exception as e:
            print(f"解压缩失败: {e}")
            return compressed_data
    def get(self, url: str, headers: Optional[Dict[str, str]] = None, cookies: Optional[str] = None, response_headers: Optional[Dict[str, str]] = None,proxy: Optional[Dict[str, str]] = None) -> Optional[str]:
        """
        发送 GET 请求
        :param url: 请求的 URL
        :param headers: 请求头
        :param cookies: cookies
        :param proxy: 代理设置
        :return: 返回的响应数据
        """
        buffer = BytesIO()
        header_buffer = BytesIO()
        result = None
        http_code = 0
        erro = {
            "code": 0,
            "msg": ''
        }
        try:
            self.c.setopt(pycurl.URL, url)
            self.c.setopt(pycurl.HTTPGET, 1)
            self.c.setopt(pycurl.FOLLOWLOCATION, 0)
            if headers:
                self.c.setopt(pycurl.HTTPHEADER, [f"{key}: {value}" for key, value in headers.items()])
            if cookies:
                self.c.setopt(pycurl.COOKIE, cookies)
            # 处理代理（HTTP/HTTPS/SOCKS5）
            if proxy:
                proxy_type = proxy.get("type", "socket5").lower()
                proxy_host = proxy.get("ip", "")
                proxy_port = proxy.get("port", 0)
                proxy_user = proxy.get("user", "")
                proxy_pass = proxy.get("pass", "")
                if len(proxy_host) > 0 and proxy_port > 0:
                    # 设置代理类型
                    if proxy_type == "socket5":
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5)
                    elif proxy_type in ("http", "https"):
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
                    # 设置代理地址和认证
                    self.c.setopt(pycurl.PROXY, f"{proxy_host}:{proxy_port}")
                    if proxy_user and proxy_pass:
                        self.c.setopt(pycurl.PROXYUSERPWD, f"{proxy_user}:{proxy_pass}")
            if url.lower().startswith("https"):
                self.c.setopt(pycurl.SSL_VERIFYPEER, 0)
                self.c.setopt(pycurl.SSL_VERIFYHOST, 0)
            self.c.setopt(pycurl.HEADERFUNCTION, header_buffer.write)
            self.c.setopt(pycurl.WRITEFUNCTION, buffer.write)
            self.c.perform()
            http_code = self.c.getinfo(pycurl.RESPONSE_CODE)
            # 解析响应头（如果 return_headers 参数存在）
            if response_headers is not None:
                headers_text = header_buffer.getvalue().decode('utf-8')
                for line in headers_text.splitlines():
                    if ':' in line:
                        key, value = line.split(':', 1)
                        response_headers[key.strip()] = value.strip()
            result = buffer.getvalue().decode('utf-8')
        except Exception as e:
            errno, errstr = e.args
            erro['code'] = errno
            erro['msg'] = errstr
            print(f"GET 请求失败: {e}")
        finally:
            #self.c.close()
            buffer.close()
            header_buffer.close()
        return result,http_code,erro

    def post(
            self,
            url: str,
            data: Optional[Union[Dict[str, Any], bytes]] = None,
            headers: Optional[Dict[str, str]] = None,
            cookies: Optional[str] = None,
            response_headers: Optional[Dict[str, str]] = None,  # 新增参数，用于返回响应头
            proxy: Optional[Dict[str, str]] = None,
            timeout: int = 30,  # 新增超时参数，默认60秒
            enable_Http2=False,
    ) -> Optional[Union[str,bytes]]:
        """
        发送 POST 请求
        :param url: 请求的 URL
        :param data: 请求体数据
        :param headers: 请求头
        :param cookies: cookies
        :param proxy: 代理设置
        :param return_headers: 用于接收响应头的字典（可选）
        :return: 返回的响应数据（如果 return_headers 不为 None，则响应头会存入该字典）
        """
        buffer = BytesIO()
        header_buffer = BytesIO()

        #self.c.setopt(self.c.VERBOSE, True)
        # if not self.c:
        #     self.c =pycurl.Curl()
        result=None
        http_code=0
        erro={
            "code" : 0,
            "msg":''
        }

        try:
            # # 关键多进程优化配置
            self.c.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)  # 强制 IPv4 避免线程问题
            self.c.setopt(pycurl.DNS_CACHE_TIMEOUT, 0)  # 禁用 DNS 缓存
            self.c.setopt(pycurl.FORBID_REUSE, 0)
            self.c.setopt(pycurl.FRESH_CONNECT, 0)

            self.c.setopt(pycurl.POST, 1)
            if enable_Http2:
                self.c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_2_0)  # 1. 强制使用HTTP/2且不允许降级
                self.c.setopt(pycurl.SSL_ENABLE_ALPN, 1)
            else:
                self.c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_1_1)
            self.c.setopt(pycurl.CONNECTTIMEOUT, int(timeout/3))
            self.c.setopt(pycurl.TIMEOUT, timeout)
            self.c.setopt(pycurl.URL, url)
            #self.c.setopt(pycurl.ENCODING,'deflate, gzip')

            if headers:
                self.c.setopt(pycurl.HTTPHEADER, [f"{key}: {str(value)}" for key, value in headers.items()])
            if len(data)>0:
                self.c.setopt(pycurl.POSTFIELDS, data)
            if cookies:
                self.c.setopt(pycurl.COOKIE, cookies)
            # 处理代理（HTTP/HTTPS/SOCKS5）
            if proxy and self.lastProxy != proxy: #
                proxy_type = proxy.get("type", "socket5").lower()
                proxy_host = proxy.get("ip", "")
                proxy_port = proxy.get("port", 0)
                proxy_user = proxy.get("user", "")
                proxy_pass = proxy.get("pass", "")
                self.lastProxy = proxy
                self.c.setopt(pycurl.FRESH_CONNECT, 1) #强制重连
                if len(proxy_host) > 0 and proxy_port > 0:
                    # 设置代理类型
                    if proxy_type == "socket5":
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5)
                    elif proxy_type in ("http", "https"):
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
                    # 设置代理地址和认证
                    self.c.setopt(pycurl.PROXY, f"{proxy_host}:{proxy_port}")
                    if proxy_user and proxy_pass:
                        self.c.setopt(pycurl.PROXYUSERPWD, f"{proxy_user}:{proxy_pass}")
            # if url.lower().startswith("https"):
            #     self.c.setopt(pycurl.SSL_VERIFYPEER, 0)
            #     self.c.setopt(pycurl.SSL_VERIFYHOST, 0)

            # 设置回调函数，捕获响应头
            self.c.setopt(pycurl.HEADERFUNCTION, header_buffer.write)
            self.c.setopt(pycurl.WRITEFUNCTION, buffer.write)
            buffer.seek(0)
            buffer.truncate(0)
            header_buffer.seek(0)
            header_buffer.truncate(0)
            self.c.perform()
            http_code = self.c.getinfo(pycurl.RESPONSE_CODE)
            # 解析响应头（如果 return_headers 参数存在）
            if response_headers is not None:
                headers_text = header_buffer.getvalue().decode('utf-8')
                for line in headers_text.splitlines():
                    if ':' in line:
                        key, value = line.split(':', 1)
                        response_headers[key.strip()] = value.strip()
            # 获取响应数据
            raw_data = buffer.getvalue()
            # 检查是否需要解压缩
            if response_headers:
                content_encoding = response_headers.get('content-encoding', '').lower()
                if content_encoding and raw_data:
                     raw_data = self.decompress_data(raw_data, content_encoding)
            if data is not None:
                if isinstance(data, bytes):
                    result= raw_data
                else:
                    result = raw_data.decode('utf-8')
        except Exception as e:
            errno, errstr = e.args
            erro['code']=errno
            erro['msg']=errstr
            print(f"POST 请求失败: {e}")
            # self.c.close()
            # self.c=None
        finally:
            # self.c.close()
            # self.c = None
            buffer.close()
            header_buffer.close()
        return result,http_code,erro
    def put(
            self,
            url: str,
            data: Optional[Union[Dict[str, Any], bytes]] = None,
            headers: Optional[Dict[str, str]] = None,
            cookies: Optional[str] = None,
            response_headers: Optional[Dict[str, str]] = None,  # 新增参数，用于返回响应头
            proxy: Optional[Dict[str, str]] = None,
            timeout: int = 30,  # 新增超时参数，默认60秒
            enable_Http2=False,
    ) -> Optional[Union[str,bytes]]:
        """
        发送 POST 请求
        :param url: 请求的 URL
        :param data: 请求体数据
        :param headers: 请求头
        :param cookies: cookies
        :param proxy: 代理设置
        :param return_headers: 用于接收响应头的字典（可选）
        :return: 返回的响应数据（如果 return_headers 不为 None，则响应头会存入该字典）
        """
        buffer = BytesIO()
        header_buffer = BytesIO()

        #self.c.setopt(self.c.VERBOSE, True)
        # if not self.c:
        #     self.c =pycurl.Curl()
        result=None
        http_code=0
        erro={
            "code" : 0,
            "msg":''
        }

        try:
            # # 关键多进程优化配置
            self.c.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)  # 强制 IPv4 避免线程问题
            self.c.setopt(pycurl.DNS_CACHE_TIMEOUT, 0)  # 禁用 DNS 缓存
            self.c.setopt(pycurl.FORBID_REUSE, 0)
            self.c.setopt(pycurl.FRESH_CONNECT, 0)

            self.c.setopt(pycurl.CUSTOMREQUEST, 'PUT')
            if enable_Http2:
                self.c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_2_0)  # 1. 强制使用HTTP/2且不允许降级
                self.c.setopt(pycurl.SSL_ENABLE_ALPN, 1)
            else:
                self.c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_1_1)
            self.c.setopt(pycurl.CONNECTTIMEOUT, int(timeout/3))
            self.c.setopt(pycurl.TIMEOUT, timeout)
            self.c.setopt(pycurl.URL, url)
            #self.c.setopt(pycurl.ENCODING,'deflate, gzip')

            if headers:
                self.c.setopt(pycurl.HTTPHEADER, [f"{key}: {str(value)}" for key, value in headers.items()])
            if len(data)>0:
                self.c.setopt(pycurl.POSTFIELDS, data)
            if cookies:
                self.c.setopt(pycurl.COOKIE, cookies)
            # 处理代理（HTTP/HTTPS/SOCKS5）
            if proxy and self.lastProxy != proxy: #
                proxy_type = proxy.get("type", "socket5").lower()
                proxy_host = proxy.get("ip", "")
                proxy_port = proxy.get("port", 0)
                proxy_user = proxy.get("user", "")
                proxy_pass = proxy.get("pass", "")
                self.lastProxy = proxy
                self.c.setopt(pycurl.FRESH_CONNECT, 1) #强制重连
                if len(proxy_host) > 0 and proxy_port > 0:
                    # 设置代理类型
                    if proxy_type == "socket5":
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5)
                    elif proxy_type in ("http", "https"):
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
                    # 设置代理地址和认证
                    self.c.setopt(pycurl.PROXY, f"{proxy_host}:{proxy_port}")
                    if proxy_user and proxy_pass:
                        self.c.setopt(pycurl.PROXYUSERPWD, f"{proxy_user}:{proxy_pass}")
            if url.lower().startswith("https"):
                self.c.setopt(self.c.SSL_VERIFYPEER, 0)
                self.c.setopt(self.c.SSL_VERIFYHOST, 0)

            # 设置回调函数，捕获响应头
            self.c.setopt(pycurl.HEADERFUNCTION, header_buffer.write)
            self.c.setopt(pycurl.WRITEFUNCTION, buffer.write)
            buffer.seek(0)
            buffer.truncate(0)
            header_buffer.seek(0)
            header_buffer.truncate(0)
            self.c.perform()
            http_code = self.c.getinfo(pycurl.RESPONSE_CODE)
            # 解析响应头（如果 return_headers 参数存在）
            if response_headers is not None:
                headers_text = header_buffer.getvalue().decode('utf-8')
                for line in headers_text.splitlines():
                    if ':' in line:
                        key, value = line.split(':', 1)
                        response_headers[key.strip()] = value.strip()
            # 获取响应数据
            raw_data = buffer.getvalue()
            # 检查是否需要解压缩
            if response_headers:
                content_encoding = response_headers.get('content-encoding', '').lower()
                if content_encoding and raw_data:
                     raw_data = self.decompress_data(raw_data, content_encoding)
            if data is not None:
                if isinstance(data, bytes):
                    result= raw_data
                else:
                    result = raw_data.decode('utf-8')
        except Exception as e:
            errno, errstr = e.args
            erro['code']=errno
            erro['msg']=errstr
            print(f"POST 请求失败: {e}")
            # self.c.close()
            # self.c=None
        finally:
            # self.c.close()
            # self.c = None
            buffer.close()
            header_buffer.close()
        return result,http_code,erro
    def get_image(self, url: str, headers: Optional[Union[Dict[str, Any], bytes]] = None, cookies: Optional[str] = None, proxy: Optional[Dict[str, str]] = None) -> Optional[str]:
        """
        发送 GET 请求并返回 base64 编码的图片
        :param url: 请求的 URL
        :param headers: 请求头
        :param cookies: cookies
        :param proxy: 代理设置
        :return: 返回的 base64 编码的图片
        """
        buffer = BytesIO()
        # c = pycurl.Curl()  # 已移动到__init__
        try:
            self.c.setopt(self.c.URL, url)
            if headers:
                self.c.setopt(self.c.HTTPHEADER, [f"{key}: {value}" for key, value in headers.items()])
            if cookies:
                self.c.setopt(self.c.COOKIE, cookies)
            # 处理代理（HTTP/HTTPS/SOCKS5）
            if proxy:
                proxy_type = proxy.get("type", "socket5").lower()
                proxy_host = proxy.get("ip", "")
                proxy_port = proxy.get("port", 0)
                proxy_user = proxy.get("user", "")
                proxy_pass = proxy.get("pass", "")
                if len(proxy_host) > 0 and proxy_port > 0:
                    # 设置代理类型
                    if proxy_type == "socks5":
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5)
                    elif proxy_type in ("http", "https"):
                        self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
                    # 设置代理地址和认证
                    self.c.setopt(pycurl.PROXY, f"{proxy_host}:{proxy_port}")
                    if proxy_user and proxy_pass:
                        self.c.setopt(pycurl.PROXYUSERPWD, f"{proxy_user}:{proxy_pass}")
            if url.lower().startswith("https"):
                self.c.setopt(self.c.SSL_VERIFYPEER, 0)
                self.c.setopt(self.c.SSL_VERIFYHOST, 0)
            self.c.setopt(self.c.WRITEFUNCTION, buffer.write)
            self.c.perform()
            #self.c.close()
            import base64
            return base64.b64encode(buffer.getvalue()).decode('utf-8')
        except Exception as e:
            print(f"GET 图片请求失败: {e}")
            return None

    def download(self, url: str, path: str, progress_callback: Optional[Callable] = None) -> Optional[str]:
        """
        下载文件
        :param url: 请求的 URL
        :param path: 保存文件的路径
        :param progress_callback: 进度回调函数
        :return: 返回的响应数据
        """
        buffer = BytesIO()
        # c = pycurl.Curl()  # 已移动到__init__
        try:
            with open(path, 'wb') as f:
                self.c.setopt(self.c.URL, url)
                if url.lower().startswith("https"):
                    self.c.setopt(self.c.SSL_VERIFYPEER, 0)
                    self.c.setopt(self.c.SSL_VERIFYHOST, 0)
                self.c.setopt(self.c.WRITEFUNCTION, f.write)
                if progress_callback:
                    self.c.setopt(self.c.XFERINFOFUNCTION, progress_callback)
                    self.c.setopt(self.c.NOPROGRESS, 0)
                self.c.perform()
                #self.c.close()
            return "Download successful"
        except Exception as e:
            print(f"下载文件失败: {e}")
            return None